/*
 * scanner.c - wifi scanner and bluetooth scanner
 */

#include "bpcommon.h"

/**
 * addrcmp - compare two hashed 5-byte addresses
 * @ha_local: local address
 * @ha_peer: peer address
 * Returns: 0 if cs == ct; <0 if ha_local < ha_peer; >0 if ha_local > ha_peer
 */
int addrcmp(char *ha_local, char *ha_peer)
{
	return memcmp(ha_local, ha_peer, HASH_ADDR_LEN);
}

/**
 * addrhash - convert regular mac address into its 5-byte hash address which is recognizable in ASCII format
 * @mac_addr: pointer to source mac address 
 * @hash_addr: pointer to resulting hash address
 * Returns: none
 */
void addrhash(u_char *mac_addr, u_char *hash_addr)
{
	unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
	unsigned int hash = 0;
 	int n = ETHER_ADDR_LEN;
	
	while (n--)	{
		hash = hash * seed + (*mac_addr++);
	}
	hash = hash & 0x7FFFFFFF;

	hash_addr[0] = (hash % 96) + 32; 
	hash_addr[1] = ((hash / 96) % 96) + 32;
	hash_addr[2] = ((hash / (96 * 96)) % 96) + 32;
	hash_addr[3] = ((hash / (96 * 96 * 96)) % 96) + 32;
	hash_addr[4] = ((hash / (96 * 96 * 96 * 96)) % 96) + 32;
	
}

/** 
  * scanner_wifi_free - destroy the wireless scan linked list
  * @wsh - a pointer to the wireless_scan_head
  * returns: 0 on success; -1 on failure
  */
static int scanner_wifi_free(struct wireless_scan_head *wsh)
{
	struct wireless_scan *next;
	struct wireless_scan *current;
	
	current = wsh->result;
	while (current){
		next = current->next;
		free(current);
		current = next; 
	}
	wsh->result = NULL;
	return 0;
}

/** 
  * scanner_wifi - initiate Wi-Fi scans and update local fingerprint from rssi scan
  * @struct rssi_map *rssimap - a pointer to a rssi fingerprint map
  * @int scan_num - flag specify the scan duration
  * returns: 0 on success; -1 on failure
  */
int scanner_wifi(struct rssi_map *rssimap, int stscan)
{
	struct configparam *config = rssimap->globalconfig;
	struct rssi_fgpt *localfgpt = rssimap->fgpt_head;
	struct wireless_scan_head wsh;
	int skfd, round, ret, scan_num;

	/* for localization using short scan of SHORT_SAMPLE_COUNT rounds to reduce delay*/
	/* for beacon establishment using regular scan of WIFI_SAMPLE_COUNT rounds to increase accuracy */
	/* argument passed by caller can override configuration */
	scan_num = ((config->shortscan)&&(stscan)) ? SHORT_SAMPLE_COUNT : WIFI_SAMPLE_COUNT; 
	char isshortscan[10];	
	if (scan_num == SHORT_SAMPLE_COUNT) 
		strcpy(isshortscan, "short");
	else
		strcpy(isshortscan, "standard");
	printf("Start %s Wi-Fi scanning at %s\n", isshortscan, config->wifi_ifname);

	skfd = iw_sockets_open();

	for (round = 0; round < scan_num; round++){
		if (iw_scan(skfd, config->wifi_ifname, WE_VERSION, &wsh))
			return -1;

		ret = rssifgpt_update_wifi(localfgpt, &wsh, round, scan_num);
		scanner_wifi_free(&wsh);
	}
	
	iw_sockets_close(skfd);

	return 0;

}

/** 
  * scanner_bt - initiate Bluetooth inquiry and extract fingerprints from EIR data
  * @struct rssi_map *rssimap - a pointer to a rssi fingerprint map
  * returns: 0 on success; -1 on failure
  */
int scanner_bt(struct rssi_map *rssimap, int inq_duration)
{
	inquiry_info *devices = NULL;
	bdaddr_t local;
	uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
	int max_rsp, num_rsp, adapter_id, sock, len, flags, i, bconcount;
	char name[249] = { 0 };			/* EIR data buffer */
	struct rssi_fgpt *newbcon;

	/* create HCI socket */
	adapter_id = hci_get_route(NULL);
	sock = hci_open_dev(adapter_id);
	if ((adapter_id < 0)||(sock < 0)){
		perror("opening socket");
		exit(1);
	}
	
	/* start inquiry */
	len = inq_duration; 
	max_rsp = 255; 
	flags = IREQ_CACHE_FLUSH; 
	devices = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
	printf("start inquiry with duration %2.2lf secs\n", len * 1.28);
	num_rsp = hci_inquiry(adapter_id, len, max_rsp, lap, &devices, flags);
	if (num_rsp < 0){
		perror("hci_inquiry");
		exit(1);
	}

	/* read name received in eir data (/var/lib/bluetooth/$local_addr/eir) */
	printf("start name resolution\n");
	hci_devba(adapter_id, &local);
	rssifgpt_flush_bcon(rssimap->fgpt_tail);
	newbcon = rssimap->fgpt_tail->next;
	bconcount = 0;
	for (i = 0; i < num_rsp; i++){
		memset(name, 0, sizeof(name));
		/* read eir */
		read_remote_eir(&local, &(devices+i)->bdaddr, name);
		/* only extract the type we defined */
		if ((name[1] == BLUEPIN_EIR_TYPE) && (!memcmp(name+2, "#$#", 3))){
			rssifgpt_update_bcon(newbcon, name);
			bconcount++;
			newbcon = newbcon->next;
		}
	}
	
	free(devices);
	close(sock);
	
	return bconcount;

}
